#pragma once
#include "Macros.h"
#include <atlbase.h> // Common COM helpers.
#include <d3d12.h>
#include <dxcapi.h>      // Be sure to link with dxcompiler.lib.
#include <d3d12shader.h> // Shader reflection.
#include <cassert>
#include <vector>
namespace SL
{

class SELAH_API ShaderReflector
{
public:
    enum ShaderType
    {
        Unknown,
        Vertex,
        Pixel,
        Geometry,
        // Hull,
        // Domain,
        Compute,
        RayTracing,
        // RayGeneration,
        // Intersection,
        // AnyHit,
        // ClosestHit,
        // Miss,
        // Callable
        ShaderTypeCount
    };

public:
    static ShaderReflector& getInstance()
    {
        static ShaderReflector instance;
        return instance; // initialized once, thread-safe per C++11 standard
    }
    ShaderReflector(const ShaderReflector&) = delete;
    ShaderReflector& operator=(const ShaderReflector&) = delete;

    void reflectShader(LPCWSTR shaderFile);
    ID3DBlob* compileShaderToObj(LPCWSTR shaderFile, ShaderType shaderType);
    IDxcBlobUtf8* compileShaderToDisasm(LPCWSTR shaderFile, ShaderType shaderType);

private:
    ShaderReflector();
    ~ShaderReflector();

    IDxcUtils* pUtils;        // load shaderfile into blob
    IDxcCompiler3* pCompiler; // responsible for compiling the shader.
    IDxcIncludeHandler* pIncludeHandler;

    std::vector<LPCWSTR> buildCompileArgs(LPCWSTR,ShaderType shaderType, bool);
    ShaderType getShaderType(LPCWSTR);
    CComPtr<IDxcResult> compileShader(LPCWSTR shaderFile, ShaderType);
    void printResourceBindings(ShaderType, IDxcBlob*, IDxcUtils*);
};

constexpr const char* to_string(D3D_SHADER_INPUT_TYPE type)
{
    switch (type)
    {
    case D3D_SIT_CBUFFER:
        return "D3D_SIT_CBUFFER";
    case D3D_SIT_TBUFFER:
        return "D3D_SIT_TBUFFER";
    case D3D_SIT_TEXTURE:
        return "D3D_SIT_TEXTURE";
    case D3D_SIT_SAMPLER:
        return "D3D_SIT_SAMPLER";
    case D3D_SIT_UAV_RWTYPED:
        return "D3D_SIT_UAV_RWTYPED";
    case D3D_SIT_STRUCTURED:
        return "D3D_SIT_STRUCTURED";
    case D3D_SIT_UAV_RWSTRUCTURED:
        return "D3D_SIT_UAV_RWSTRUCTURED";
    case D3D_SIT_BYTEADDRESS:
        return "D3D_SIT_BYTEADDRESS";
    case D3D_SIT_UAV_RWBYTEADDRESS:
        return "D3D_SIT_UAV_RWBYTEADDRESS";
    case D3D_SIT_UAV_APPEND_STRUCTURED:
        return "D3D_SIT_UAV_APPEND_STRUCTURED";
    case D3D_SIT_UAV_CONSUME_STRUCTURED:
        return "D3D_SIT_UAV_CONSUME_STRUCTURED";
    case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER:
        return "D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER";
    case D3D_SIT_RTACCELERATIONSTRUCTURE:
        return "D3D_SIT_RTACCELERATIONSTRUCTURE";
    case D3D_SIT_UAV_FEEDBACKTEXTURE:
        return "D3D_SIT_UAV_FEEDBACKTEXTURE";
    default:
        return "UNKNOWN";
    }
}
} // namespace SL